﻿#include "timer.h"

Timer::~Timer()
{
	m_mtx.lock();
	m_running = false;
	m_cv.notify_one();
	m_mtx.unlock();
	m_thread.join();
}

int Timer::schedule(TimerCallback &&action,
	int delay, size_t data, bool repeated)
{
	if (delay < 0) return 0;
	std::lock_guard<std::mutex> lock(m_mtx);
	int id = m_seed == INT_MAX ? 1 : m_seed;
	while (m_tasks.find(id) != m_tasks.end()) {
		if (++id == INT_MAX) id = 1;
	}
	m_seed = id + 1;
	auto time = now() + std::chrono::milliseconds(delay);
	TimerTask task{ repeated, id, delay, time, data, action };
	m_tasks.emplace(id, task);
	m_cv.notify_one();
	return id;
}

bool Timer::cancel(int id)
{
	std::lock_guard<std::mutex> lock(m_mtx);
	auto itr = m_tasks.find(id);
	bool find = (itr != m_tasks.end());
	if (find) m_tasks.erase(itr);
	m_cv.notify_one();
	return find;
}

void Timer::proceduce()
{
	while (m_running) {
		std::unique_lock<std::mutex> lock(m_mtx);
		while (m_tasks.empty()) {
			m_cv.wait(lock);
			if (!m_running) return;
		}
		TimerTask &task = m_tasks.begin()->second;
		for (auto &item : m_tasks) {
			if (item.second.time < task.time) {
				task = item.second;
			}
		}
		int id = task.id;
		std::chrono::milliseconds delay = task.time - now();
		task.time += std::chrono::milliseconds(task.delay);
		if (std::cv_status::timeout == m_cv.wait_for(lock, delay)) {
			auto itr = m_tasks.find(id);
			if (itr != m_tasks.end()) {
				TimerCallback callback = itr->second.action;
				size_t data = itr->second.data;
				if (!task.repeated) {
					m_tasks.erase(id);
				}
				lock.unlock();
				callback(id, data);
			}
		}
	}
}

